feat(search): make the workspace index policy visible and correct - #2368
Merged
wgqqqqq merged 1 commit intoAug 18, 2026
Merged
Conversation
wgqqqqq
force-pushed
the
feat/workspace-search-index-strategy
branch
from
August 18, 2026 10:35
d768261 to
6efba7d
Compare
Vendors flashgrep v0.2.15, aligns BitFun with the daemon's new base-snapshot
protocol, and closes the gaps that made the managed index either wrong or
inscrutable from the UI.
Protocol (breaking upstream change): `RepoStatus.rebuild_recommended` is gone,
replaced by base-delta fields; `SearchParams.allow_scan_fallback` and the
BitFun-only `QuerySpec.before_context`/`after_context` are gone too. The old
required field would have failed every status-bearing response, so the binary
bump and the protocol change land together. The "rebuild recommended" badge
becomes "index catching up", driven by `base_advance_target_head`.
Line text: the daemon's four search modes return positions only, so BitFun was
rendering line numbers as content ("path:73:line 73"). Content search now goes
through `search/grouped_line_matches` and hydrates text from disk, locally via
`workspace_search/line_hydration.rs` and over SSH via a single batched awk pass
(`remote_line_hydration.rs`) rather than one round trip per file. Both paths
share `services-core::filesystem::content_preview`.
Auto-index policy: index only workspaces with roughly 2000+ indexable files.
Below that the measured win is 8 ms to 0.2 ms while the index inflates worst
(3.3x). The file count is gathered in two commands because
`--others --exclude-standard` costs 4.2 s on chromium against 89 ms for
`--cached`, so the tracked pass returns early once the threshold is met.
Builds run through a queue with a cross-workspace disk budget.
Policy visibility: the daemon reports `needs_index` both while the policy is
still evaluating and after it declined, so the UI could only hedge. The
decision now rides along with the status (`WorkspaceIndexStatus.auto_index`),
and a small workspace reads "no index needed" with the count and threshold
instead of an ambiguous sentence that never changes. Remote workspaces have no
BitFun-side policy and report `None`.
A workspace that is not a Git worktree can never be indexed, which is a
property of the folder rather than a fault, so it stays on the neutral
indicator instead of turning red.
Grep's `-A`/`-B`/`-C` route to ripgrep, because the daemon has no context-line
support and the flags were previously dropped on the wire without a word.
`ContentSearchRequest` loses its context-line fields entirely rather than
keeping them unread: a field that looks like it carries context but is silently
discarded is exactly how the original defect happened, so the indexed path can
no longer express the request at all.
wgqqqqq
force-pushed
the
feat/workspace-search-index-strategy
branch
from
August 18, 2026 11:00
6efba7d to
c928460
Compare
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
What this does
Vendors flashgrep v0.2.15, aligns BitFun with the daemon's new base-snapshot protocol, and closes the gaps that left the managed index either computing the wrong thing or impossible to interpret from the UI.
Every number quoted below is measured, not estimated; the methodology and raw runs live outside this PR.
Why the binary and the protocol have to move together
This is a breaking upstream change:
RepoStatus.rebuild_recommendedis gone, replaced by the base-delta fields;SearchParams.allow_scan_fallbackand the BitFun-onlyQuerySpec.before_context/after_contextare gone too.BitFun declared
rebuild_recommendedas required, so dropping in the new binary alone makes every status-bearing response fail to deserialize —open_repo,searchandgloball become unusable. Confirmed by an stdio probe against the v0.2.12 binary.The "rebuild recommended" badge loses its data source and becomes "index catching up", driven by
base_advance_target_head.Line text: we were rendering line numbers as content
All four of the daemon's search modes return positions only (
LineMatch { path, line_number }, no line text). BitFun rendered the line number as if it were the text, so Grep emittedpath:73:line 73and the desktop search preview showed "line 73".Content search now goes through
search/grouped_line_matchesand hydrates text from disk:workspace_search/line_hydration.rs): truncate tomax_resultsbefore reading, open each file exactly once, do the reads inspawn_blocking.remote_ssh/workspace_search/remote_line_hydration.rs): the local "one open per file" shape cannot be carried over — on SSH that is one round trip per file, i.e. 250 serial round trips at the defaulthead_limit=250. Instead the request ("which files, which lines of each") is written as a manifest and fed to a singleawkpass. 250 matches measured at 4 commands or fewer, typically 1.services-core::filesystem::content_preview, so rendering consistency comes from shared code rather than two parallel implementations.Auto-index threshold: roughly 2000 indexable files
The inputs to that decision have asymmetric cost, so the count runs in two commands:
git ls-files --cachedtakes 89 ms on chromium, while adding--others --exclude-standardtakes 4250 ms. The tracked pass returns early once the threshold is met and never pays for the untracked walk.Command::output()waits for the process to exit, so an earlybreakonly helps if the commands are split.Builds go through a queue with a cross-workspace disk budget.
The decision has to be visible
The daemon reports
needs_indexboth while the policy is still evaluating a workspace and after it has deliberately declined, so BitFun could only write a sentence that was true either way. The user saw a state that never changed, with no way to tell "stuck" from "nothing should happen here".The policy's decision now travels with the status (
WorkspaceIndexStatus.auto_index), so a small workspace reads "No index needed — only 216 files, below the 2000 threshold; searching directly is faster."One caveat: the count
breaks as soon as the threshold is reached, soEligiblecarries a lower bound, not a real count. Only theBelowThresholdbranch has a true number — which is exactly the branch that needs to display one. TheEligiblecopy says "at least N".Remote SSH has no BitFun-side auto-index policy at all (the remote daemon decides), so it reports
Noneand the frontend falls back to the previous wording.A non-Git workspace no longer shows a red indicator
flashgrep refuses to open a directory that is not a Git worktree with a HEAD commit. That is a property of the folder, not an index fault, so the raw daemon error is normalized into a stable BitFun-owned sentence, the indicator stays neutral gray, and content search silently falls back.
Grep's -A / -B / -C
The daemon has no context-line support, and the two fields BitFun was sending were its own invention which the daemon ignored — meaning
-A/-B/-Cwere silently doing nothing. Requests asking for context lines now route to ripgrep.render_workspace_search_content_linesis kept for when the daemon gains support.Verification
cargo check --workspacecleancargo test -p bitfun-services-integrations --features workspace-search --lib— 27 passedcargo test -p bitfun-services-integrations --features workspace-search --test workspace_search_contracts -- --ignored— starts a real daemon, indexes a temporary Git repo, and asserts the results carry real line text rather than "line N" placeholdersshell_testsfeed the generated command tosh -cand run realawk. The same script is byte-identical on BWK awk 20200816 (macOS), GNU Awk 5.4.1 and mawk 1.3.4, covering CRLF, embedded tabs, multibyte text, empty files, out-of-range line numbers, missing files and long-line truncationtsc --noEmit,eslint,vitest run src/tools/file-explorer(12 passed)Not included
relay-service/db.rs,terminal/transcript.rs, and others) are unrelated to this work and were left out.